use std::io;
use std::fs::DirEntry;
use std::fs::read_dir;

mod Util {
    fn str_split_by_whitespaces(s: &str) -> Vec<&str> {
        Regex::new(r"\s+").split(s).collect()
    }

    fn read_dir_exclude_ordered<P: AsRef<Path>, V: AsRef<Vec<&str>>>(path: P, exclude_filenames: V) -> io::Result<Vec<DirEntry>> {
        let mut entries: Vec<DirEntry> = read_dir(path)?
            .filter(|e| exclude_filenames.contains(&e.file_name()))
            .collect::<Result<_, _>>()?;
        entries.sort_unstable_by_key(|x| x.file_name());
        Ok(entries)
    }

    fn force_delete(path: P) -> Result<()> {
        fs::remove_file(&path)?
        fs::remove_dir_all(&path)?
    }
}

mod JsonUtil {
    fn load(path: &str) {
        let f = File::open(path)?;
        let reader = BufReader::new(&f)?;
        return serde_json::from_reader(&reader)?;
    }

    fn object_get_bool(map: serde_json::Object, key: &String, default_value: bool) -> Option<bool> {
        data.get(key).map_or(Some(default_value), |x| x.as_bool())
    }
    
    fn object_get_i32(map: serde_json::Object, key: &String, default_value: i32) -> Option<i32> {
        data.get(key).map_or(Some(default_value), |x| x.as_i32())
    }
    
    fn object_get_path(map: serde_json::Object, key: &String, default_value: &str) -> Option<&str> {
        match data.get(key) {
            Some(value) => {
                match value.as_str() {
                    Some(x) => Path::new(x)
                    None => None
                }
            }
            None => Some(Path::new(default_value))
        };
    }
    
    fn object_get_object(map: serde_json::Object, key: &String) -> Option<&serde_json::Object> {
        match data.get(key) {
            Some(value) => value.as_object()
            None => serde_json::json!({}).as_object()
        };
    }
}
